﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;

namespace APP.Win32
{
    public class Win32Helper
    {
        [StructLayout(LayoutKind.Sequential)]
        public struct RECT
        {
            public int Left;
            public int Top;
            public int Right;
            public int Bottom;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct POINT
        {
            public int X;
            public int Y;

            public POINT(int x, int y)
            {
                this.X = x;
                this.Y = y;
            }
        }

        [DllImport("user32.dll")]
        public static extern IntPtr GetTopWindow(IntPtr hWnd);

        [DllImport("user32.dll")]
        public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);

        [DllImport("user32.dll")]
        public static extern IntPtr WindowFromPoint(POINT point);

        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        public static extern bool GetCursorPos(out POINT pt);

        [DllImport("User32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        public static extern int GetWindowThreadProcessId(IntPtr hwnd, out int processId);

        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect);

        [DllImport("user32.dll", ExactSpelling = true, CharSet = CharSet.Auto)]
        public static extern IntPtr GetParent(IntPtr hWnd);
        [DllImport("user32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
        public static extern int GetWindowTextLength(IntPtr hWnd);

        [DllImport("user32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
        // 外面不要直接调用GetWindowText，获取本进程的窗口名的时候，可能会发生卡死。请调用: GetWindowTextSkipCurrentProcess
        // 参考: https://www.cnblogs.com/xylc/p/3552467.html
        private static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);


        [DllImport("user32.dll", EntryPoint = "GetKeyboardState")]
        public static extern int GetKeyboardState(byte[] pbKeyState);

        private const string dummy_current_iBot_processName = "DummyIbotWinName";
        private static int current_process_id = 0;
        public static int GetCurrentProcessId()
        {
            if (0 == current_process_id)
            {
                current_process_id = System.Diagnostics.Process.GetCurrentProcess().Id;
            }
            return current_process_id;
        }

        public static bool IsCurrentProcessHWnd(IntPtr hWnd)
        {
            int hwndProcessId;
            GetWindowThreadProcessId(hWnd, out hwndProcessId);
            bool isCurrent = GetCurrentProcessId() == hwndProcessId;
            return isCurrent;
        }

        public static int GetWindowTextSkipCurrentProcess(IntPtr hWnd, StringBuilder lpString, int nMaxCount, out bool isCurrentProcessWnd)
        {
            int length;
            isCurrentProcessWnd = IsCurrentProcessHWnd(hWnd);
            if (isCurrentProcessWnd)
            {
                lpString.Append(dummy_current_iBot_processName);
                length = dummy_current_iBot_processName.Length;
            }
            else
            {
                length = GetWindowText(hWnd, lpString, nMaxCount);
            }
            return length;
        }

        [DllImport("user32.dll")]
        public static extern void keybd_event
        (
            byte bVk,// 虚拟键值  
            byte bScan,// 硬件扫描码  
            uint dwFlags,// 动作标识  
            IntPtr dwExtraInfo// 与键盘动作关联的辅加信息  
        );



        [DllImport("user32.dll")]
        public static extern bool SetProcessDPIAware();

        #region DPI
        [DllImport("user32.dll")]
        public static extern IntPtr GetDC(IntPtr ptr);

        [DllImport("user32.dll", EntryPoint = "ReleaseDC")]
        public static extern IntPtr ReleaseDC(IntPtr hWnd, IntPtr hDc);

        [DllImport("gdi32.dll")]
        public static extern IntPtr CreateDC(
            string lpszDriver,  // driver name
            string lpszDevice,  // device name
            string lpszOutput,  // not used; should be NULL
            Int64 lpInitData    // optional printer data
        );

        [DllImport("gdi32.dll")]
        public static extern int GetDeviceCaps(
            IntPtr hdc,         // handle to DC
            int nIndex          // index of capability
        );

        #endregion

        #region DPI
        enum DeviceCap
        {
            VERTRES = 10,
            PHYSICALWIDTH = 110,
            SCALINGFACTORX = 114,
            DESKTOPVERTRES = 117,

            // http://pinvoke.net/default.aspx/gdi32/GetDeviceCaps.html
        }
        private const int LOGPIXELSX = 88;
        private const int LOGPIXELSY = 90;
        /// <summary>
        /// 获取当前DPI缩放比例
        /// </summary>
        /// <returns></returns>
        public static DpiScaleF GetCurrentDpiScale()
        {
            DpiScaleF scaleUI = new DpiScaleF(1.0f, 1.0f);
            try
            {
                Win32Helper.SetProcessDPIAware();
                IntPtr screenDC = Win32Helper.GetDC(IntPtr.Zero);
                int dpi_x = Win32Helper.GetDeviceCaps(screenDC, LOGPIXELSX);
                int dpi_y = Win32Helper.GetDeviceCaps(screenDC, LOGPIXELSY);

                int dpi_y1 = Win32Helper.GetDeviceCaps(screenDC, (int)DeviceCap.VERTRES);
                int dpi_y2 = Win32Helper.GetDeviceCaps(screenDC, (int)DeviceCap.PHYSICALWIDTH);
                int dpi_y3 = Win32Helper.GetDeviceCaps(screenDC, (int)DeviceCap.SCALINGFACTORX);
                int dpi_y4 = Win32Helper.GetDeviceCaps(screenDC, (int)DeviceCap.DESKTOPVERTRES);


                scaleUI.X = (float)dpi_x / 96.0f;
                scaleUI.Y = (float)dpi_y / 96.0f;
                Win32Helper.ReleaseDC(IntPtr.Zero, screenDC);
                return scaleUI;
            }
            catch (System.Exception ex)
            {
            }

            return scaleUI;
        }

        public struct DpiScaleF
        {
            public DpiScaleF(float x, float y)
            {
                this.X = x;
                this.Y = y;
            }
            public float X { get; set; }

            public float Y { get; set; }

            public bool IsScale()
            {
                return this.X > 1 || this.Y > 1;
            }

            public Point ApplyScale(int srcX, int srcY)
            {
                int destX = Convert.ToInt32(this.X * srcX);
                int destY = Convert.ToInt32(this.Y * srcY);
                return new Point(destX, destY);
            }
        }
        #endregion
    }
}
